home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / emacs.lha / emacs-19.16 / lisp / life.el < prev    next >
Lisp/Scheme  |  1993-06-09  |  10KB  |  288 lines

  1. ;;; life.el --- John Horton Conway's `Life' game for GNU Emacs
  2.  
  3. ;; Copyright (C) 1988 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Kyle Jones <talos!kjones@uunet.uu.net>
  6. ;; Keywords: games
  7.  
  8. ;; This file is part of GNU Emacs.
  9.  
  10. ;; GNU Emacs is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 2, or (at your option)
  13. ;; any later version.
  14.  
  15. ;; GNU Emacs is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. ;; GNU General Public License for more details.
  19.  
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  22. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. ;;; Commentary:
  25.  
  26. ;; A demonstrator for John Horton Conway's "Life" cellular automaton
  27. ;; in Emacs Lisp.  Picks a random one of a set of interesting Life
  28. ;; patterns and evolves it according to the familiar rules.
  29.  
  30. ;;; Code:
  31.  
  32. (defconst life-patterns
  33.   [("@@@" " @@" "@@@")
  34.    ("@@@ @@@" "@@  @@ " "@@@ @@@")
  35.    ("@@@ @@@" "@@   @@" "@@@ @@@")
  36.    ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
  37.    ("@@@@@@@@@@")
  38.    ("   @@@@@@@@@@       "
  39.     "     @@@@@@@@@@     "
  40.     "       @@@@@@@@@@   "
  41.     "@@@@@@@@@@          "
  42.     "@@@@@@@@@@          ")
  43.    ("@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@" "@")
  44.    ("@               @" "@               @"  "@               @"
  45.     "@               @" "@               @"  "@               @"
  46.     "@               @" "@               @"  "@               @"
  47.     "@               @" "@               @"  "@               @"
  48.     "@               @" "@               @"  "@               @")
  49.    ("@@               " " @@              " "  @@             "
  50.     "   @@            " "    @@           " "     @@          "
  51.     "      @@         " "       @@        " "        @@       "
  52.     "         @@      " "          @@     " "           @@    "
  53.     "            @@   " "             @@  " "              @@ "
  54.     "               @@")
  55.    ("@@@@@@@@@" "@   @   @" "@ @@@@@ @" "@ @   @ @" "@@@   @@@" 
  56.     "@ @   @ @" "@ @@@@@ @" "@   @   @" "@@@@@@@@@")]
  57.   "Vector of rectangles containing some Life startup patterns.")
  58.  
  59. ;; Macros are used macros for manifest constants instead of variables
  60. ;; because the compiler will convert them to constants, which should
  61. ;; eval faster than symbols.
  62. ;;
  63. ;; Don't change any of the life-* macro constants unless you thoroughly
  64. ;; understand the `life-grim-reaper' function.
  65.  
  66. (defmacro life-life-char () ?@)
  67. (defmacro life-death-char () (1+ (life-life-char)))
  68. (defmacro life-birth-char () 3)
  69. (defmacro life-void-char () ?\ )
  70.  
  71. (defmacro life-life-string () (char-to-string (life-life-char)))
  72. (defmacro life-death-string () (char-to-string (life-death-char)))
  73. (defmacro life-birth-string () (char-to-string (life-birth-char)))
  74. (defmacro life-void-string () (char-to-string (life-void-char)))
  75. (defmacro life-not-void-regexp () (concat "[^" (life-void-string) "\n]"))
  76.  
  77. ;; try to optimize the (goto-char (point-min)) & (goto-char (point-max))
  78. ;; idioms.  This depends on goto-char's not griping if we undershoot
  79. ;; or overshoot beginning or end of buffer.
  80. (defmacro goto-beginning-of-buffer () '(goto-char 1))
  81. (defmacro maxint () (lsh (lsh (lognot 0) 1) -1))
  82. (defmacro goto-end-of-buffer () '(goto-char (maxint)))
  83.  
  84. (defmacro increment (variable) (list 'setq variable (list '1+ variable)))
  85.  
  86.  
  87. ;; list of numbers that tell how many characters to move to get to
  88. ;; each of a cell's eight neighbors.
  89. (defconst life-neighbor-deltas nil)
  90.  
  91. ;; window display always starts here.  Easier to deal with than
  92. ;; (scroll-up) and (scroll-down) when trying to center the display.
  93. (defconst life-window-start nil)
  94.  
  95. ;; For mode line
  96. (defconst life-current-generation nil)
  97. ;; Sadly, mode-line-format won't display numbers.
  98. (defconst life-generation-string nil)
  99.  
  100. (defun abs (n) (if (< n 0) (- n) n))
  101.  
  102. ;;;###autoload
  103. (defun life (&optional sleeptime)
  104.   "Run Conway's Life simulation.
  105. The starting pattern is randomly selected.  Prefix arg (optional first
  106. arg non-nil from a program) is the number of seconds to sleep between
  107. generations (this defaults to 1)."
  108.   (interactive "p")
  109.   (or sleeptime (setq sleeptime 1))
  110.   (life-setup)
  111.   (life-display-generation sleeptime)
  112.   (catch 'life-exit
  113.     (while t
  114.       (let ((inhibit-quit t))
  115.     (life-grim-reaper)
  116.     (life-expand-plane-if-needed)
  117.     (life-increment-generation)
  118.     (life-display-generation sleeptime)))))
  119.  
  120. (defalias 'life-mode 'life)
  121. (put 'life-mode 'mode-class 'special)
  122.  
  123. (random t)
  124.  
  125. (defun life-setup ()
  126.   (let (n)
  127.     (switch-to-buffer (get-buffer-create "*Life*") t)
  128.     (erase-buffer)
  129.     (kill-all-local-variables)
  130.     (setq case-fold-search nil
  131.       mode-name "Life"
  132.       major-mode 'life-mode
  133.       truncate-lines t
  134.       life-current-generation 0
  135.       life-generation-string "0"
  136.       mode-line-buffer-identification '("Life: generation "
  137.                         life-generation-string)
  138.       fill-column (1- (window-width))
  139.       life-window-start 1)
  140.     (buffer-disable-undo (current-buffer))
  141.     ;; stuff in the random pattern
  142.     (life-insert-random-pattern)
  143.     ;; make sure (life-life-char) is used throughout
  144.     (goto-beginning-of-buffer)
  145.     (while (re-search-forward (life-not-void-regexp) nil t)
  146.       (replace-match (life-life-string) t t))
  147.     ;; center the pattern horizontally
  148.     (goto-beginning-of-buffer)
  149.     (setq n (/ (- fill-column (save-excursion (end-of-line) (point))) 2))
  150.     (while (not (eobp))
  151.       (indent-to n)
  152.       (forward-line))
  153.     ;; center the pattern vertically
  154.     (setq n (/ (- (1- (window-height))
  155.           (count-lines (point-min) (point-max)))
  156.            2))
  157.     (goto-beginning-of-buffer)
  158.     (newline n)
  159.     (goto-end-of-buffer)
  160.     (newline n)
  161.     ;; pad lines out to fill-column
  162.     (goto-beginning-of-buffer)
  163.     (while (not (eobp))
  164.       (end-of-line)
  165.       (indent-to fill-column)
  166.       (move-to-column fill-column)
  167.       (delete-region (point) (progn (end-of-line) (point)))
  168.       (forward-line))
  169.     ;; expand tabs to spaces
  170.     (untabify (point-min) (point-max))
  171.     ;; before starting be sure the automaton has room to grow
  172.     (life-expand-plane-if-needed)
  173.     ;; compute initial neighbor deltas
  174.     (life-compute-neighbor-deltas)))
  175.  
  176. (defun life-compute-neighbor-deltas ()
  177.   (setq life-neighbor-deltas
  178.     (list -1 (- fill-column)
  179.           (- (1+ fill-column)) (- (+ 2 fill-column))
  180.           1 fill-column (1+ fill-column)
  181.           (+ 2 fill-column))))
  182.  
  183. (defun life-insert-random-pattern ()
  184.   (insert-rectangle
  185.    (elt life-patterns (% (abs (random)) (length life-patterns))))
  186.   (insert ?\n))
  187.  
  188. (defun life-increment-generation ()
  189.   (increment life-current-generation)
  190.   (setq life-generation-string (int-to-string life-current-generation)))
  191.  
  192. (defun life-grim-reaper ()
  193.   ;; Clear the match information.  Later we check to see if it
  194.   ;; is still clear, if so then all the cells have died.
  195.   (store-match-data nil)
  196.   (goto-beginning-of-buffer)
  197.   ;; For speed declare all local variable outside the loop.
  198.   (let (point char pivot living-neighbors list)
  199.     (while (search-forward (life-life-string) nil t)
  200.       (setq list life-neighbor-deltas
  201.         living-neighbors 0
  202.         pivot (1- (point)))
  203.       (while list
  204.     (setq point (+ pivot (car list))
  205.           char (char-after point))
  206.     (cond ((eq char (life-void-char))
  207.            (subst-char-in-region point (1+ point)
  208.                      (life-void-char) 1 t))
  209.           ((< char 3)
  210.            (subst-char-in-region point (1+ point) char (1+ char) t))
  211.           ((< char 9)
  212.            (subst-char-in-region point (1+ point) char 9 t))
  213.           ((>= char (life-life-char))
  214.            (increment living-neighbors)))
  215.     (setq list (cdr list)))
  216.       (if (memq living-neighbors '(2 3))
  217.       ()
  218.     (subst-char-in-region pivot (1+ pivot)
  219.                 (life-life-char) (life-death-char) t))))
  220.   (if (null (match-beginning 0))
  221.       (life-extinct-quit))
  222.   (subst-char-in-region 1 (point-max) 9 (life-void-char) t)
  223.   (subst-char-in-region 1 (point-max) 1 (life-void-char) t)
  224.   (subst-char-in-region 1 (point-max) 2 (life-void-char) t)
  225.   (subst-char-in-region 1 (point-max) (life-birth-char) (life-life-char) t)
  226.   (subst-char-in-region 1 (point-max) (life-death-char) (life-void-char) t))
  227.  
  228. (defun life-expand-plane-if-needed ()
  229.   (catch 'done
  230.     (goto-beginning-of-buffer)
  231.     (while (not (eobp))
  232.       ;; check for life at beginning or end of line.  If found at
  233.       ;; either end, expand at both ends,
  234.       (cond ((or (eq (following-char) (life-life-char))
  235.          (eq (progn (end-of-line) (preceding-char)) (life-life-char)))
  236.          (goto-beginning-of-buffer)
  237.          (while (not (eobp))
  238.            (insert (life-void-char))
  239.            (end-of-line)
  240.            (insert (life-void-char))
  241.            (forward-char))
  242.        (setq fill-column (+ 2 fill-column))
  243.        (scroll-left 1)
  244.        (life-compute-neighbor-deltas)
  245.        (throw 'done t)))
  246.       (forward-line)))
  247.   (goto-beginning-of-buffer)
  248.   ;; check for life within the first two lines of the buffer.
  249.   ;; If present insert two lifeless lines at the beginning..
  250.   (cond ((search-forward (life-life-string)
  251.              (+ (point) fill-column fill-column 2) t)
  252.      (goto-beginning-of-buffer)
  253.      (insert-char (life-void-char) fill-column)
  254.      (insert ?\n)
  255.      (insert-char (life-void-char) fill-column)
  256.      (insert ?\n)
  257.      (setq life-window-start (+ life-window-start fill-column 1))))
  258.   (goto-end-of-buffer)
  259.   ;; check for life within the last two lines of the buffer.
  260.   ;; If present insert two lifeless lines at the end.
  261.   (cond ((search-backward (life-life-string)
  262.               (- (point) fill-column fill-column 2) t)
  263.      (goto-end-of-buffer)
  264.      (insert-char (life-void-char) fill-column)
  265.      (insert ?\n)
  266.      (insert-char (life-void-char) fill-column)
  267.      (insert ?\n)
  268.      (setq life-window-start (+ life-window-start fill-column 1)))))
  269.  
  270. (defun life-display-generation (sleeptime)
  271.   (goto-char life-window-start)
  272.   (recenter 0)
  273.   
  274.   ;; Redisplay; if the user has hit a key, exit the loop.
  275.   (or (eq t (sit-for sleeptime))
  276.       (throw 'life-exit nil)))
  277.  
  278. (defun life-extinct-quit ()
  279.   (life-display-generation 0)
  280.   (signal 'life-extinct nil))
  281.  
  282. (put 'life-extinct 'error-conditions '(life-extinct quit))
  283. (put 'life-extinct 'error-message "All life has perished")
  284.  
  285. (provide 'life)
  286.  
  287. ;;; life.el ends here
  288.